﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TextMining;

namespace HierarchicalClustering.Metrics
{
    public class Cosine : IMetric   // cosine similarity
    {
        private int _numOfDocuments;

        public Cosine(int numOfDocuments)
        {
            _numOfDocuments = numOfDocuments;
        }

        #region IMetric Members

        public double Distance(TextMining.Document documentA, TextMining.Document documentB)
        {
            IEnumerator<KeyValuePair<string, Pair>> enumA = documentA.Words.GetEnumerator();
            IEnumerator<KeyValuePair<string, Pair>> enumB = documentB.Words.GetEnumerator();
            bool flagA = true, flagB = true;
            double sum = 0, temp, temp2, normA = 0, normB = 0, result;

            enumA.MoveNext();
            enumB.MoveNext();
            if (enumA.Current.Value == null)   // no word found in the 1st document
                flagA = false;
            if (enumB.Current.Value == null)   // no word found in the 2nd document
                flagB = false;
            while (flagA != false && flagB != false)   // until both documents have no more words
            {
                if (flagA == true && enumA.Current.Value.Key.IsImportant == false)
                {   // not important word found in the 1st document
                    flagA = enumA.MoveNext();
                    continue;
                }
                if (flagB == true && enumB.Current.Value.Key.IsImportant == false)
                {   // not important word found in the 2nd document
                    flagB = enumB.MoveNext();
                    continue;
                }
                if (flagA == false || enumA.Current.Key.CompareTo(enumB.Current.Key) < 0)
                {   // the 1st document has no more words or the 1st document's word is smaller than the 2nd document's
                    temp = enumA.Current.Value.Key.TfIdfValue(enumA.Current.Value.Value, documentA.WordCount, _numOfDocuments);
                    normA += temp * temp;
                    flagA = enumA.MoveNext();
                }
                else if (flagB == false || enumA.Current.Key.CompareTo(enumB.Current.Key) > 0)
                {   // the 2nd document has no more words or the 2nd document's word is smaller than the 1st document's
                    temp = enumB.Current.Value.Key.TfIdfValue(enumB.Current.Value.Value, documentB.WordCount, _numOfDocuments);
                    normB += temp * temp;
                    flagB = enumB.MoveNext();
                }
                else   // the same word is found in both documents
                {
                    temp = enumA.Current.Value.Key.TfIdfValue(enumA.Current.Value.Value, documentA.WordCount, _numOfDocuments);
                    temp2 = enumB.Current.Value.Key.TfIdfValue(enumB.Current.Value.Value, documentB.WordCount, _numOfDocuments);
                    sum += temp * temp2;
                    normA += temp * temp;
                    normB += temp2 * temp2;
                    flagA = enumA.MoveNext();
                    flagB = enumB.MoveNext();
                }
            }
            result = sum / Math.Sqrt(normA * normB);   // cosine similarity
            return 1 - result;   // reversed cosine similarity (the nearer to 1, the further they are)
        }

        #endregion
    }
}
